//
//  GRFMDownloader.m
//  GRFMDownloader_Example
//
//  Created by Guo Rui on 2018/1/11.
//  Copyright © 2018年 GR. All rights reserved.
//

#import "GRFMDownloader.h"
#import "GRFMFileTool.h"
#define kDownloadedPath [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"downloadCompletion"]

#define kDownloadingPath NSTemporaryDirectory()


@interface GRFMDownloader()<NSURLSessionDataDelegate> {
    NSString *_downloadCompletionPath;
    NSString *_downloadingPath;
    long long _tmpSize;
    long long _totalSize;
    NSOutputStream *_output;
    NSURLSessionDataTask *_dataTask;
}

@property (strong, nonatomic) NSURLSession *session;
@property (assign, nonatomic) GRFMDownloaderState state;

@property (copy, nonatomic) DownloadInfoBlock infoBlock;
@property (copy, nonatomic) DownloadStateChangeBlock stateChangeBlock;
@property (copy, nonatomic) DownloadProgressChangeBlock progressChangeBlock;
@property (copy, nonatomic) DownloadSuccessBlock successBlock;
@property (copy, nonatomic) DownloadFailedBlock failedBlock;

@property (strong, nonatomic) NSURL *url;
@end

@implementation GRFMDownloader

#pragma mark - Public
- (void)download:(NSURL *)url info:(DownloadInfoBlock)info progressChange:(DownloadProgressChangeBlock)progressChange stateChange:(DownloadStateChangeBlock)stateChange success:(DownloadSuccessBlock)success faile:(DownloadFailedBlock)faile
{
    
    self.infoBlock = info;
    self.progressChangeBlock = progressChange;
    self.stateChangeBlock = stateChange;
    self.successBlock = success;
    self.failedBlock = faile;
    
    [self download:url];
}

- (void)download:(NSURL *)url
{
    NSLog(@"DownloadedPath - %@", kDownloadedPath);
    if ([_dataTask.originalRequest.URL isEqual:url]) {
        // 继续下载
        if (self.state == GRFMDownloaderStatePause) {
            [self resume];
            return;
        }
        // 下载失败应该继续往下走。
    }
    
    self.url = url;
    
    // 到达这里，1. _dataTask 为nil 2. url 不同, 这时应该取消之前的下载.
    [self cancel];
    
    // 开始下载
    // 1. 文件已下载，存在downloadCompletion文件夹中
    if (![GRFMFileTool fileExists:kDownloadedPath]) {
        [GRFMFileTool createDirectory:kDownloadedPath];
    }
    _downloadCompletionPath = [kDownloadedPath stringByAppendingPathComponent:url.lastPathComponent];
    if ([GRFMFileTool fileExists:_downloadCompletionPath]) {
        self.state = GRFMDownloaderStateDownloadSuccess;
        return;
    }
    
    // 2. 文件未下载
    // 2.1 在downloading文件夹中不存在 -> 下载
    _downloadingPath = [kDownloadingPath stringByAppendingString:url.lastPathComponent];
    if (![GRFMFileTool fileExists:_downloadingPath]) {
        [self download:url offset:0];
        return;
    }
    
    // 2.2 在downloading文件夹中存在
    _tmpSize = [GRFMFileTool fileSize:_downloadingPath];
    [self download:url offset:_tmpSize];
}

// Note: [_dataTask suspend] 执行2次，那么[_dataTask resume]也需要执行2次. 所以需要引入标志位控制。
- (void)pause
{
    if (self.state == GRFMDownloaderStateDownloading) {
        self.state = GRFMDownloaderStatePause;
        [_dataTask suspend];
    }
}

// Note: [_dataTask resume] 执行2次，那么[_dataTask suspend]也需要执行2次. 所以需要引入标志位控制。
- (void)resume
{
    if (self.state == GRFMDownloaderStatePause || self.state == GRFMDownloaderStateUnKnow) {
        [_dataTask resume];
        self.state = GRFMDownloaderStateDownloading;
    }
}

- (void)cancel
{
    NSLog(@"%@ - 任务取消", self.url);
    self.state = GRFMDownloaderStatePause;
    [self.session invalidateAndCancel];
    self.session = nil;
    _dataTask = nil;
}

- (void)cancelAndClear
{
    [self cancel];
    [GRFMFileTool removeFile:_downloadingPath];
}


#pragma mark - NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSHTTPURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
    // 获取文件的总大小
    // Content-Range中的大小为准确值，如果响应头中没有该字段，就取Content-Length。
    // Content-Length表示的剩余文件大小.
    NSString *range = [response.allHeaderFields objectForKey:@"Content-Range"];
    if (range == nil || range.length == 0) {
        _totalSize = [[response.allHeaderFields objectForKey:@"Content-Length"] longLongValue];
    }
    else {
        _totalSize = [[[range componentsSeparatedByString:@"/"] lastObject] longLongValue];
    }
    
    if (self.infoBlock) {
        self.infoBlock(_totalSize);
    }
    
    // 2.2.1 文件大小 > 总大小, 删除， 重新下载
    if (_tmpSize > _totalSize) {
        if ([GRFMFileTool removeFile:_downloadingPath]) {
            [self download:response.URL offset:0];
        }
        else {
            [self download:response.URL];
        }
        completionHandler(NSURLSessionResponseCancel);
        return;
    }
    
    // 2.2.2 文件大小 = 总大小， 移动到downloadCompletion文件夹中
    if (_tmpSize == _totalSize) {
        [GRFMFileTool moveFile:_downloadingPath toPath:_downloadCompletionPath];
//        NSLog(@"移动到downloadCompletion文件夹中");
        self.state = GRFMDownloaderStateDownloadSuccess;
        completionHandler(NSURLSessionResponseCancel);
        return;
    }
    
    // 2.2.3 文件大小 < 总大小, 从断点处开始下载
//    NSLog(@"从断点处开始下载");
    self.state = GRFMDownloaderStateDownloading;
    
    _output = [NSOutputStream outputStreamToFileAtPath:_downloadingPath append:YES];
    [_output open];
    
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
//    NSLog(@"接收数据");
    _tmpSize += data.length;
    self.progress = 1.0 * _tmpSize / _totalSize;
    
    [_output write:data.bytes maxLength:data.length];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
{
//    NSLog(@"接收完成");
    long long size = [GRFMFileTool fileSize:_downloadingPath];
    if (_totalSize == size) {
//        NSLog(@"移动到完成目录中去了");
        // TODO: 文件完整性检查
        [GRFMFileTool moveFile:_downloadingPath toPath:_downloadCompletionPath];
        
        self.state = GRFMDownloaderStateDownloadSuccess;
    }
    else {
        // code = -999 是cancel后的错误码
        if (error.code != -999) {
            self.state = GRFMDownloaderStateDownloadFailed;
        }
    }
}

#pragma mark - Private
- (void)download:(NSURL *)url offset:(long long)offset
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:0];
    [request setValue:[NSString stringWithFormat:@"bytes=%lld-", offset] forHTTPHeaderField:@"Range"];
    
    _dataTask = [self.session dataTaskWithRequest:request];
    [self resume];
}

#pragma mark - Getter
- (NSURLSession *)session
{
    if (!_session) {
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}


- (void)setState:(GRFMDownloaderState)state
{
    if (_state == state) {
        return;
    }
    _state = state;
    
    if (self.stateChangeBlock) {
        self.stateChangeBlock(state);
    }
    
    if (state == GRFMDownloaderStateDownloadSuccess) {
        if (self.successBlock) {
            self.successBlock(_dataTask.originalRequest.URL, _downloadCompletionPath);
        }
    }
    
    if (state == GRFMDownloaderStateDownloadFailed) {
        if (self.failedBlock) {
            self.failedBlock();
        }
    }
}

- (void)setProgress:(float)progress
{
    _progress = progress;
    
    if (self.progressChangeBlock) {
        self.progressChangeBlock(self.url, progress);
    }
}

@end
